<?php
namespace Tcp\Controller;

define("WEBSOCKET_SERVER_DEBUG", false);

use \Think\Controller;
use GuzzleHttp\json_encode;

class WebsocketController extends Controller
{
    private $serv = null;
    public function run()
    {
        if (! IS_CLI) {
            exit();
        }
        
        $this->redisClear();
        
        $this->serv = new \swoole_websocket_server('0.0.0.0', 7711);
        
        $this->serv->set(array(
            'worker_num' => 8,
            'daemonize' => ! WEBSOCKET_SERVER_DEBUG,
        ));
        
        $this->serv->on('Open', function ($serv, $req)
        {
            $webId = md5(($req->fd).time());
            $redis = $this->redisInit();
            $redis->Set("socket_fd_webid:{$req->fd}", $webId);
            $redis->Set("socket_webid_fd:$webId", $req->fd);
            $this->outputLog("Connect:{$req->fd}:$webId");
        });
        
        $this->serv->on('WorkerStart', function ($serv, $worker_id)
        {
            // 不是task进程（即为worker进程）
            if (! $this->serv->taskworker) {
                $this->serv->tick(1000, function () use($serv)
                {
                    $redis = $this->redisInit();
                    // 取出一个消息包进行处理，一个消息包包括消息接收者(必须)、消息内容(必须)
                    $msgPacket = $redis->lPop("socket_message");
                    if (! $msgPacket) {
                        return;
                    }
                    $msgPacket = json_decode($msgPacket, true);
                    if (! is_array($msgPacket)) {
                        $this->outputLog("Msg format error");
                        return;
                    }
                    if (empty($msgPacket['webId']) || empty($msgPacket['msg'])) {
                        $this->outputLog("Msg parameter error");
                        return;
                    }
                    
                    $webId = $msgPacket['webId'];
                    $fd = $redis->Get("socket_webid_fd:$webId");
                    if(!$fd || !$serv->exist($fd))
                    {
                        $this->outputLog("Websocket client not connected:$fd:$webId");
                        return;
                    }

                    $result = empty($msgPacket['msg'])?'':$msgPacket['msg'];
                    if(!is_string($result))
                    {
                        $result = json_encode($result, JSON_UNESCAPED_UNICODE);
                    }
                    $this->outputLog("Msg:$fd:$webId:$result");
                    $this->serv->push($fd, $result);
                });
            }
        });
        
        $this->serv->on('Message', function ($serv, $frame)
        {
            $this->outputLog("Receive:{$frame->fd}:{$frame->data}");
            $data = json_decode($frame->data, true);
            if (! isset($data['cmd']) || ! (isset($data['args']))) {
                return;
            }
            $args = $data['args'];
            if(empty($args['userId']))
            {
                return;
            }
            $userId = $args['userId'];
            // TODO: 校验：WEB是否已登录，登录用户是否可使用该蓝牙
            // 客户端应用程序是否登录
            if(!checkClientLogin($userId))
            {
                $this->serv->push($frame->fd, $this->getMsgString('client_unlogin', null));
                $this->outputLog("Tcp client for qrmaster not connected:$userId");
                return;
            }                    
            $redis = $this->redisInit();
            $webId = $redis->Get("socket_fd_webid:{$frame->fd}");
            if(empty($webId))
            {
                $this->outputLog("Websocket client disconnected:{$frame->fd}");
                return;
            }
            switch ($data['cmd']) {
                case 'read_btcard':
                case 'cancel_btcard':
                    if(empty($args['mac']))
                    {
                        return;
                    }
                    if(empty($args['deviceCommand']))
                    {
                        return;
                    }
                    $cmd = 'read_btcard';
                    sendTcpClientMsg($userId, $cmd, array(
                        'webId' => $webId,
                        'isCancel' => ($data['cmd']=='cancel_btcard'),
                        'mac' => $args['mac'],
                        'deviceCommand' => $args['deviceCommand'],
                    ));
                    $this->outputLog("sendTcpClientMsg:$userId:$webId:$cmd:{$args['mac']}:{$args['deviceCommand']}");
                    break;
                case "read_rfcard":
                case "cancel_rfcard":
                    if(empty($args['cardType']))
                    {
                        return;
                    }
                    $cmd = 'read_rfcard'.($args['cardType']==3?'_qrm1':'_t57m1');
                    sendTcpClientMsg($userId, $cmd, array(
                        'webId' => $webId,
                        'isCancel' => ($data['cmd']=='cancel_rfcard'),
                    ));
                    $this->outputLog("sendTcpClientMsg:$userId:$webId:$cmd");
                    break;
            }
        });
        
        $this->serv->on('Close', function ($serv, $fd)
        {
            $redis = $this->redisInit();
            $webId = $redis->Get("socket_fd_webid:$fd");
            if ($webId) {
                $redis->Del("socket_webid_fd:$webId");
                $redis->Del("socket_fd_webid:$fd");
            }
            $this->outputLog("Close:$fd:$webId");
        });
        
        $this->serv->start();
    }

    private function getMsgString($cmd, $args)
    {
        $msg = array();
        $msg['cmd'] = $cmd;
        $msg['args'] = $args;
        return json_encode($msg, JSON_UNESCAPED_UNICODE);
    }
    
    private function redisInit($dbIndex = 14)
    {
        $redis = new \Redis();
        $redis->connect(C('REDIS_SERVERIP'), C('REDIS_SERVERPORT'), 30);
        $redis->auth(C('REDIS_AUTH_PASSWORD'));
        $redis->select($dbIndex);
        return $redis;
    }
    
    private function redisClear()
    {
        $redis = $this->redisInit();
        $keys = $redis->Keys('socket_fd_webid:*');
        $redis->Del($keys);
        $keys = $redis->Keys('socket_webid_fd:*');
        $redis->Del($keys);
    }
    
    private function outputLog($info)
    {
        $info = date('Y-m-d H:i:s') . " $info\n";
        error_log($info, 3, LOG_PATH . 'Websocket_' . date('Y_m_d') . '.log');
        if (WEBSOCKET_SERVER_DEBUG) {
            echo $info;
        }
    }
}
